﻿//#define CodeCompletion
#define NewParserAPI
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using ICSharpCode.AvalonEdit.CodeCompletion;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Editing;
using ICSharpCode.AvalonEdit.Highlighting;
using IWMAG.Scripting.Compiler;
using IWMAG.Scripting.Interpreter;
using System.Windows.Input;
using IWMAG.Scripting.DevelopmentEnvironment.CodeCompletion;



namespace IWMAG.Scripting.DevelopmentEnvironment
{
    /// <summary>
    /// Interaction logic for EditWindow.xaml
    /// </summary>
    public partial class EditWindow : Window
    {
        const string CompilerThrewException = "Compiler Crashed";
        BugWindow bw = new BugWindow();
        public EditWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            mainEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("iwmag");

#if CodeCompletion
            this.dawg = (Application.Current as App).dawg;
#endif

            mainEditor.Text = "int boot()\n{\n\tlog \"Hello World!!!\";\n\treturn 0;\n}";
            Compile();
            backGroundCompiler = new Task(() =>
            {
               
                while (!backgroundCompilationCanceler.IsCancellationRequested)
                {
                    try
                    {
                        textChangedSemaphore.Wait(backgroundCompilationCanceler.Token);
                    }
                    catch (OperationCanceledException)
                    {
                        return;
                    }
                   
                    
                   
                    Compile();
  

                }
            }, backgroundCompilationCanceler.Token);
            backGroundCompiler.Start();



            mainEditor.Document.TextChanged += new EventHandler(mainEditor_TextChanged);
#if CodeCompletion
            mainEditor.TextArea.TextEntered += textEditor_TextArea_TextEntered;
            mainEditor.TextArea.TextEntering += textEditor_TextArea_TextEntering;
#endif

            this.Closing += new CancelEventHandler(EditWindow_Closing);
        }
#if CodeCompletion
        CompletionWindow GetCompletionWindow()
        {
            if (completionWindow != null)
            {
                return completionWindow;
            }

            var k = new CompletionWindow(mainEditor.TextArea);
            k.Closing += (sender, e) =>
                {
                    completionWindow = null;
                };

            return k;
        }


        CompletionWindow completionWindow;

        void textEditor_TextArea_TextEntered(object sender, TextCompositionEventArgs e)
        {
            if (e.Text.Length > 0 && char.IsLetter(e.Text[0]))
            {
                dawg.Push(e.Text[0]);
            }
            else
            {
                dawg.Reset();
            }
            var win = GetCompletionWindow();
            var t = win.CompletionList.CompletionData;
            t.Clear();
            foreach (var item in dawg.EnumerateListWithPrevious())
            {
                t.Add(new MyCompletionData(new String(item.ToArray())));
            }
           win.Show();
           if (t.Count == 0)
           {
               win.Close();
           }
            
        }

        void textEditor_TextArea_TextEntering(object sender, TextCompositionEventArgs e)
        {
            
        }

        DirectedAcyclicList<char> dawg;
#endif
        void EditWindow_Closing(object sender, CancelEventArgs e)
        {
            bw.Close();
            backgroundCompilationCanceler.Cancel();
        }

        void mainEditor_TextChanged(object sender, EventArgs e)
        {
            textChangedSemaphore.Set();
        }

        ScriptEngine engine;
        void Compile()
        {
            compilingSemaphore.Wait();
            compilingSemaphore.Reset();
            ITextSource snapShot = null;

            Dispatcher.Invoke(new Action(() =>
                {
                    textChangedSemaphore.Reset();
                    snapShot = mainEditor.Document.CreateSnapshot();
                    tbCompilationStaus.Text = "Compiling";
                    tbCompilationStaus.Opacity = .95;
                }));


            Exception compilerCrashed = null;
            List<IUserInfo> errors = null;
#if NewParserAPI
            engine =  null;
            try
            {
                engine = new ScriptEngine(null, snapShot.Text);
                errors = new List<IUserInfo>(engine.Compile());
            }
            catch (Exception e)
            {
              compilerCrashed = e;
            }
#else
            Parser p = new Parser();
            try
            {
                p.Parse(snapShot.Text);
                errors = new List<IUserInfo>(p.ErrorList);
            }
            catch (Exception e)
            {
                compilerCrashed = e;
            }
#endif


            UpdateErrorList(errors, compilerCrashed);

            compilingSemaphore.Set();

        }

        void UpdateErrorList(IEnumerable<IUserInfo> errorList, Exception e)
        {
            if (!Dispatcher.CheckAccess())
            {
                Dispatcher.Invoke(new Action(() =>
                {
                    UpdateErrorList(errorList, e);
                }));
                return;
            }
            errLb.Items.Clear();
            if (e != null)
            {
                errLb.Items.Add(string.Format("Compiler crashed, please file an error.\nDetails: {0}", e.Message));
                tbCompilationStaus.Text = CompilerThrewException;
                tbCompilationStaus.Opacity = 1;

                if (errorList != null)
                {
                    foreach (var item in errorList.OrderByDescending((i) => i.CanCompleteBuild.GetValueOrDefault(false).ToString()).ThenBy((i) => i.LineNumber).ThenBy((i) => i.StartIndex))
                    {
                        errLb.Items.Add(string.Format("<{0}:{1}>: {2}", item.LineNumber, item.StartIndex, item));
                    }
                }
               
            }
            else
            {

                foreach (var item in errorList.OrderByDescending((i) => i.CanCompleteBuild.GetValueOrDefault(false).ToString()).ThenBy((i) => i.LineNumber).ThenBy((i) => i.StartIndex))
                {
                    errLb.Items.Add(string.Format("<{0}:{1}>: {2}", item.LineNumber, item.StartIndex, item));
                }


                tbCompilationStaus.Text = "Compile Complete.";
                tbCompilationStaus.Opacity = .7;




            }
        }

        Task backGroundCompiler;
        CancellationTokenSource backgroundCompilationCanceler = new CancellationTokenSource();
        ManualResetEventSlim textChangedSemaphore = new ManualResetEventSlim(false);
        ManualResetEventSlim compilingSemaphore = new ManualResetEventSlim(true);

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            if (!compilingSemaphore.Wait(500))
            {
                MessageBox.Show("Compiler is busy, try again...", "iwmag");
                return;
            }
            bw.CurrentCodeText = mainEditor.Text;
            bw.Width = this.Width;
            bw.Height = this.Height;
            bw.Left = this.Left;
            bw.Top = this.Top;
            if (tbCompilationStaus.Text == CompilerThrewException)
            {
                bw.Status = BugWindow.CompilerStatus.CompileThrewError;
            }
            else if (errLb.Items.Count > 0)
            {
                bw.Status = BugWindow.CompilerStatus.CompileFailed;
            }
            else
            {
                bw.Status = BugWindow.CompilerStatus.CompiledSuccessfully;
            }
            bw.Setup();
            bw.Show();
            this.Hide();
            CancelEventHandler del = null;
            del = (s, args) =>
                 {
                     args.Cancel = true;
                     bw.Hide();
                     this.Width = bw.Width;
                     this.Height = bw.Height;
                     this.Left = bw.Left;
                     this.Top = bw.Top;
                     this.Show();
                     bw.Closing -= del;
                 };
            bw.Closing += del;
        }
        RunWindow runWindow = null;

        private void btnRun_Click(object sender, RoutedEventArgs e)
        {
            if (!compilingSemaphore.Wait(500))
	        {
                MessageBox.Show("Compiler is busy, try again...", "iwmag");
                return;
            }
            runWindow = new RunWindow();
            CancelEventHandler del = null;
            del = (s, args) =>
            {
                this.Width = runWindow.Width;
                this.Height = runWindow.Height;
                this.Left = runWindow.Left;
                this.Top = runWindow.Top;
                this.Show();
                runWindow.Closing -= del;
                runWindow = null;
            };

            runWindow.Width = this.Width;
            runWindow.Height = this.Height;
            runWindow.Left = this.Left;
            runWindow.Top = this.Top;

            runWindow.Engine = engine;

            runWindow.Closing += del;
            runWindow.Show();
            runWindow.FocusGranted();
            this.Hide();
        }
    }
}

public class MyCompletionData : ICompletionData
{
    public MyCompletionData(string text)
    {
        this.Text = text;
    }

    public System.Windows.Media.ImageSource Image
    {
        get { return null; }
    }

    public string Text { get; private set; }

    // Use this property if you want to show a fancy UIElement in the list.
    public object Content
    {
        get { return this.Text; }
    }

    public object Description
    {
        get { return "Description for " + this.Text; }
    }

    public void Complete(TextArea textArea, ISegment completionSegment,
        EventArgs insertionRequestEventArgs)
    {
        textArea.Document.Replace(completionSegment, this.Text);
    }


    public double Priority
    {
        get { return 1; }
    }
}
